// $Id$
//
// Various utility functions to avoid cluttering code as well as promote
// reuse. Several of these are taken from TAO tests/RTCORBA directory.

#include "ace/Log_Msg.h"  // for logging messages
#include "ace/Sched_Params.h"  // for scheduling params

#include "tao/ORB_Core.h" // the ORB itself
#include "tao/RTCORBA/RTCORBA.h"  // all the RTCORBA capabilities
#include "tao/RTPortableServer/RTPortableServer.h" // for RT POA

#include "TurnaroundActuator_i.h"

/**************************************************************************/
/// this function writes the object reference to a file
int
write_ior_to_file (CORBA::ORB_ptr orb,
                   Block::Turnaround::TurnaroundActuator_ptr obj,
                   const ACE_TCHAR *ior_file_name)
{
  // get the stringified reference of the object
  CORBA::String_var ior =
    orb->object_to_string (obj);

  // defensive programming - check for errors
  if (!ior.in ()) {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Actuator : Cannot acquire stringified IOR\n")));
    return -1;
  }

  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("Actuator : stringified IOR is %s\n"),
              ior.in ()));

  // create a filename
  char filename[BUFSIZ];
  ACE_OS::sprintf (filename,
                   "%s",
                   ior_file_name);

  // open a file for writing
  FILE *output_file =
    ACE_OS::fopen (filename,
                   "w");

  if (output_file == 0)
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT ("Cannot open output file for writing IOR: %s"),
                       filename),
                      -1);

  // write into the file
  ACE_OS::fprintf (output_file,
                   "%s",
                   ior.in ());

  // close the file
  ACE_OS::fclose (output_file);

  return 0;
}

/**************************************************************************/
/// returns the stringified representation of the scheduling policy
const char * sched_policy_name (int sched_policy)
{
  const char *name = 0;

  switch (sched_policy)
    {
    case ACE_SCHED_OTHER:
      name = "SCHED_OTHER";
      break;
    case ACE_SCHED_RR:
      name = "SCHED_RR";
      break;
    case ACE_SCHED_FIFO:
      name = "SCHED_FIFO";
      break;
    }

  return name;
}

/**************************************************************************/
/// a utility function to check for priority range (taken from TAO tests)
bool
check_supported_priorities (CORBA::ORB_ptr orb)
{
  int sched_policy =
    orb->orb_core ()->orb_params ()->ace_sched_policy ();

  // Check that we have sufficient priority range to run this test,
  // i.e., more than 1 priority level.
  int max_priority =
    ACE_Sched_Params::priority_max (sched_policy);
  int min_priority =
    ACE_Sched_Params::priority_min (sched_policy);

ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Max : %d ")
                  ACE_TEXT ("Min : %d \n"),
                  min_priority, max_priority));
  if (max_priority == min_priority)
    {
      ACE_DEBUG ((LM_DEBUG,
                  "Not enough priority levels with the %C scheduling policy\n"
                  "on this platform to run the test, terminating program....\n"
                  "Check svc.conf options\n",
                  sched_policy_name (sched_policy)));

      return false;
    }
  else 
    {
      ACE_DEBUG ((LM_DEBUG,
                  ACE_TEXT ("Actuator : This platform supports ")
                  ACE_TEXT ("min (%d) to max (%d) priority levels\n"),
                  min_priority, max_priority));
    }

  return true;
}

/**************************************************************************/
/// a function to create a child POA with server declared model and thread
/// pools. 
RTPortableServer::POA_ptr
create_poa_with_server_declared_model (RTCORBA::RTORB_ptr rt_orb,
                                       RTPortableServer::POA_ptr parent_poa,
                                       PortableServer::POAManager_ptr poa_manager,
                                       CORBA::Short poa_priority)
{
  // to create a child poa, we first need to create a list of policies that
  // will be the defining characteristics of the child POA being created.

  CORBA::PolicyList poa_policy_list;  // this is an unbounded sequence
  poa_policy_list.length (2); // set the size of policy list

  // now set the various policies.  See how the ORB serves as a factory
  // mechanism to create many different kinds of policies

  // first we dictate a SERVER_DECLARED model
  poa_policy_list [0] =
    rt_orb->create_priority_model_policy (RTCORBA::SERVER_DECLARED,
                                          poa_priority);

  // next we set multiple ID policy so that the same implementation of the
  // server can give rise to different obj references, each handled at a
  // different priority. 
  poa_policy_list[1] =
    parent_poa->create_id_uniqueness_policy (PortableServer::MULTIPLE_ID);

  // now create a child poa on the parent (usually the root poa)
  PortableServer::POA_var child_poa =
    parent_poa->create_POA ("Child_POA",
                            poa_manager,
                            poa_policy_list);

  // defensive programming - check for errors
  if (CORBA::is_nil (child_poa.in ())) {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Actuator : Cannot create child POA\n")));
    return RTPortableServer::POA::_nil ();
  }

  // now narrow it to a RT-enabled type
  RTPortableServer::POA_var rt_poa =
    RTPortableServer::POA::_narrow (child_poa.in ());

  if (CORBA::is_nil (child_poa.in ())) {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Actuator : Cannot narrow child POA to RT type\n")));
    return RTPortableServer::POA::_nil ();
  }

  // we return the ownership of the newly created rt child poa to the caller.
  return rt_poa._retn ();
}


/**************************************************************************/
/// a function to create a child POA with client declared model and thread
/// pools. 
RTPortableServer::POA_ptr
create_poa_with_client_declared_model (RTCORBA::RTORB_ptr rt_orb,
                                       RTPortableServer::POA_ptr parent_poa,
                                       PortableServer::POAManager_ptr poa_manager,
                                       CORBA::Short poa_priority)
{
  // to create a child poa, we first need to create a list of policies that
  // will be the defining characteristics of the child POA being created.

  CORBA::PolicyList poa_policy_list;  // this is an unbounded sequence
  poa_policy_list.length (2); // set the size of policy list

  // now set the various policies.  See how the ORB serves as a factory
  // mechanism to create many different kinds of policies

  
  RTCORBA::Priority default_server_priority = 30;
  // first we dictate a SERVER_DECLARED model
  poa_policy_list [0] =
    rt_orb->create_priority_model_policy (RTCORBA::CLIENT_PROPAGATED,
                                          default_server_priority);

  // next we set multiple ID policy so that the same implementation of the
  // server can give rise to different obj references, each handled at a
  // different priority. 
  poa_policy_list[1] =
    parent_poa->create_id_uniqueness_policy (PortableServer::MULTIPLE_ID);

  // now create a child poa on the parent (usually the root poa)
  PortableServer::POA_var child_poa =
    parent_poa->create_POA ("Child_POA",
                            poa_manager,
                            poa_policy_list);

  // defensive programming - check for errors
  if (CORBA::is_nil (child_poa.in ())) {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Actuator : Cannot create child POA\n")));
    return RTPortableServer::POA::_nil ();
  }

  // now narrow it to a RT-enabled type
  RTPortableServer::POA_var rt_poa =
    RTPortableServer::POA::_narrow (child_poa.in ());

  if (CORBA::is_nil (child_poa.in ())) {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Actuator : Cannot narrow child POA to RT type\n")));
    return RTPortableServer::POA::_nil ();
  }

  // we return the ownership of the newly created rt child poa to the caller.
  return rt_poa._retn ();
}
/**************************************************************************/
/// create an object reference and publish it externally. In this case we
/// publish it by externalizing the object reference into a string form and
/// writing it into a file.
int
create_and_publish_diag_object (CORBA::ORB_ptr orb,
                                RTPortableServer::POA_ptr poa,
                                Block_Turnaround_TurnaroundActuator_i *servant,
                                CORBA::Short priority,
                                const ACE_TCHAR *file)
{                                

  // register this implementation with the RootPOA and retrieve the
  // corresponding object ID that the ORB assigns it.
  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("Actuator : activate our impl\n")));
  PortableServer::ObjectId_var id;
  if (priority == -1) {
    // we don't override the already set priority on the POA
    id = poa->activate_object (servant);
  } else {
    // override the priority on a per obj ref level
    id = poa->activate_object_with_priority (servant, priority);
  }

  // defensive programming - check for errors
  if (id->length () == 0) {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Actuator : Cannot activate object\n")));
    return -1;
  }

  // if we registered our impl with the root POA, the default policy
  // of system-assigned ID is used.  For our child POA multiple IDs are used
  // but the IDs are still assigned by the system.  As a result what gets
  // printed here is what our ORB decided to use internally as the ID. If you
  // don't want this behavior, use a different POA and select user-defined ID
  // policy.  
  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("Actuator : Object ID after activation has length %d\n"),
              id->length ()));

  // now obtain the object reference corresponding to this ID. Recall that
  // such an obj reference is needed to register with a naming service or
  // to externalize its representation into a file. We use the second
  // approach in this code.
  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("Actuator : retrieve the obj ref for our impl\n")));
  CORBA::Object_var object = poa->id_to_reference (id.in ());

  // defensive programming - check for errors
  if (CORBA::is_nil (object.in ())) {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Actuator : Cannot acquire obj reference\n")));
    return 1;
  }

  // remember to narrow it to the right type
  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("Actuator : narrow obj ref to our type\n")));
  Block::Turnaround::TurnaroundActuator_var diag =
    Block::Turnaround::TurnaroundActuator::_narrow (object.in ());

  // defensive programming - check for errors
  if (CORBA::is_nil (diag.in ())) {
    ACE_ERROR ((LM_ERROR,
                ACE_TEXT ("Actuator : Cannot narrow obj ref to our type\n")));
    return 1;
  }

  // now write into the file. In other cases you can register this obj ref
  // with a naming service.
  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("Actuator : Write IOR to file %s\n"), file));
  int result =
    write_ior_to_file (orb,
                       diag.in (),
                       file);

  // if error, return
  if (result != 0)
    return result;

  // success
  return 0;
}
